import os
import httpx
import json
from typing import Any, Dict
from fastapi.responses import JSONResponse, StreamingResponse
from fastapi import Body, FastAPI, HTTPException, Query, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.responses import FileResponse, StreamingResponse
from app.bilibili import bilibili_source, bilibili_stream, get_bilibili, search_bilibili
from app.proxy import get_proxy, options_proxy, post_proxy, propfind_proxy
from app.music_api import get_scrape, get_popular, scrape_artists
from app.stream import stream_local_file, stream_remote_file
from app.init import CacheMiddleware, lifespan
from urllib.parse import unquote_plus
from sqlalchemy.orm import Session
from fastapi import Depends
from app.supply import log
from app.database import (
    SessionLocal,
    get_albums,
    get_artists,
    get_audios,
    get_backup,
    get_favorites,
    get_genres,
    get_lyric,
    set_lyric,
    get_playlists,
    get_search,
    set_backup,
    engine,
    Base,
)
from app.scan import (
    scan_and_extract_metadata,
    initialize_folders,
    MUSIC_PATH,
    DATA_PATH,
)


# 初始化数据
initialize_folders()

Base.metadata.create_all(bind=engine)


def get_db():
    session = SessionLocal()
    try:
        yield session
    finally:
        session.close()


app = FastAPI(lifespan=lifespan)

# 中间件注册顺序非常重要！
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    # allow_methods=["*"],
    allow_methods=["GET", "POST", "OPTIONS", "PROPFIND"],
    allow_headers=["*"],
    expose_headers=["www-authenticate"],
)
app.add_middleware(GZipMiddleware)
app.add_middleware(CacheMiddleware)


@app.get("/scan")
async def scan():
    metadata_list = scan_and_extract_metadata()
    return metadata_list


@app.get("/covers")
async def covers(request: Request, path: str):
    if path.startswith("http://") or path.startswith("https://"):
        try:
            client = request.app.state.client
            response = await client.get(path, follow_redirects=True)
            response.raise_for_status()  # 检查请求是否成功
            content_type = response.headers.get("Content-Type", "image/jpeg")
            return StreamingResponse(
                content=response.aiter_bytes(), media_type=content_type
            )
        except httpx.HTTPStatusError as e:
            raise HTTPException(
                status_code=e.response.status_code,
                detail="Failed to fetch remote image",
            )
        except Exception as e:
            raise HTTPException(status_code=500, detail="Internal server error")
    else:
        return FileResponse(path, media_type="image/jpeg")


@app.get("/stream")
async def stream(
    request: Request,
    url: str,
):
    """
    处理本地文件和远程资源的流式传输
    """
    url = unquote_plus(url)
    log(f"播放stream: {url}")
    if url.startswith("/") or os.path.isfile(url):
        return await stream_local_file(request, url)
    elif url.startswith("http://") or url.startswith("https://"):
        return await stream_remote_file(request, url)
    else:
        raise HTTPException(status_code=400, detail="Invalid URL format")


@app.get("/scrape")
async def g_scrape(request: Request):
    return await get_scrape(request)


@app.get("/lyric")
async def g_lyric(hash: int, session: Session = Depends(get_db)):
    return await get_lyric(hash=hash, session=session)


@app.post("/lyric")
async def p_lyric(data: Dict[str, Any] = Body(...)):
    return await set_lyric(data=data)


@app.get("/scrape_artists")
async def s_artists(request: Request):
    return await scrape_artists(request=request)


@app.get("/search_bilibili")
async def search_bili(request: Request, cookie: str):
    return await search_bilibili(request=request, cookie=cookie)


@app.get("/get_bilibili")
async def get_bili(request: Request):
    return await get_bilibili(request=request)


@app.get("/bilibili_source")
async def bili_source(request: Request):
    return await bilibili_source(request=request)


@app.get("/bilibili_stream")
async def bili_stream(request: Request, url: str):
    return await bilibili_stream(request=request, url=url)


@app.get("/popular")
async def popular(request: Request):
    return await get_popular(request=request)


@app.get("/audios")
async def audios(request: Request, session: Session = Depends(get_db)):
    return await get_audios(params=request.query_params, session=session)


@app.get("/albums")
async def albums(request: Request, session: Session = Depends(get_db)):
    return await get_albums(params=request.query_params, session=session)


@app.get("/artists")
async def artists(request: Request, session: Session = Depends(get_db)):
    return await get_artists(params=request.query_params, session=session)


@app.get("/genres")
async def genres(request: Request, session: Session = Depends(get_db)):
    return await get_genres(params=request.query_params, session=session)


@app.get("/playlists")
async def playlists(request: Request, session: Session = Depends(get_db)):
    return await get_playlists(params=request.query_params, session=session)


@app.get("/search")
async def search(request: Request, session: Session = Depends(get_db)):
    return await get_search(params=request.query_params, session=session)


@app.get("/favorites")
async def favorites(session: Session = Depends(get_db)):
    return await get_favorites(session=session)


@app.get("/backup")
async def g_backup(
    session: Session = Depends(get_db),
    recent: bool = Query(False),
    playlist: bool = Query(False),
):
    return await get_backup(recent=recent, playlist=playlist, session=session)


@app.post("/backup")
async def s_backup(
    request: Request,
    data: Dict[str, Any] = Body(...),
    session: Session = Depends(get_db),
):
    return await set_backup(request=request, data=data, session=session)


@app.route("/proxy", methods=["GET", "POST", "PROPFIND", "OPTIONS"])
async def f_proxy(request: Request):
    method = request.method.upper()  # 统一转为大写
    url = request.query_params.get("url")

    if not url:
        return JSONResponse(
            {"error": "Missing required parameter: url"}, status_code=400
        )

    # 统一处理 return_headers 参数
    return_headers = (
        request.query_params.get("return_headers", "true").lower() == "true"
    )

    # 使用字典映射方法处理器，避免重复代码
    method_handlers = {
        "POST": post_proxy,
        "OPTIONS": options_proxy,
        "PROPFIND": propfind_proxy,
    }

    handler = method_handlers.get(method, get_proxy)  # 默认为GET方法

    return await handler(request=request, return_headers=return_headers, url=url)


@app.get("/pic")
async def pic(request: Request, url: str):
    client = request.app.state.client
    try:
        response = await client.get(url)
        # 检查响应状态码
        if response.status_code != 200:
            raise HTTPException(status_code=response.status_code, detail=response.text)

        content_type = response.headers.get("Content-Type", "image/jpeg")

        return StreamingResponse(
            content=response.aiter_bytes(), media_type=content_type
        )

    except httpx.HTTPStatusError as e:
        return {"error": str(e)}, e.response.status_code
    except Exception as e:
        return {"error": str(e)}, 500


@app.get("/ping")
async def ping():
    return JSONResponse({"status": "ok"})


@app.get("/dict")
async def dict():
    folders = [f.name for f in DATA_PATH.iterdir() if f.is_dir()]
    return {"folders": folders}


@app.get("/file")
async def file():
    files = [
        str(f.relative_to(MUSIC_PATH)) for f in MUSIC_PATH.rglob("*") if f.is_file()
    ]
    return {"files": files}


@app.get("/debug")
async def debug(request: Request, url: str):
    return {"debug": "ok"}
    # return await get_content_length(request=request, remote_url=url)
